var environmentErrorHandleMappings = {
    //IP校验
    "proxyAvailable" : function() {
        var proxyInfo = context.get(LUA_TASK_PROXY_INFO);

        if (proxyInfo.length === 2) {
            logger.error("任务：【 {} 】代理IP异常，代理信息：{}:{}", context.get(LUA_TASK_KEY), proxyInfo[0], proxyInfo[1]);
        }
        throwBankExceptionByErrorCodeAndCustomMessage(BALO_99601_10, errorMsgMap.get(BALO_99601_10) + " : " + (proxyInfo.length == 2 ? proxyInfo[0] : "无代理"));
    },
    //网站可用性校验
    "targetWebsiteAvailable" : function() {
        logger.error("任务：【 {} 】目标网站不可用", context.get(LUA_TASK_KEY));
        throwBankExceptionByErrorCode(BALO_21401_10)
    },
    //网页控件检查
    "controlAvailable" : function() {
        logger.error("任务：【 {} 】首页控件异常", context.get(LUA_TASK_KEY));
        throwBankExceptionByErrorCode(BALO_99604_55)
    }
}

var allAvailability = {
    "proxyAvailable" : "代理可用性检查",
    "targetWebsiteAvailable" : "目标网站可用性检查",
    "controlAvailable" : "银行控件可用性检查"
}

/**
 * 验证环境，包括代理可用性、网站可用性、银行控件可用性
 *
 * @param javaFuncs
 * @returns {boolean} 环境是否可用
 */
function validateEnvironment(javaFuncs){
    //初始化环境
    initEnvironment();

    //默认代理可用
    var proxyAvailable = true
    //默认网站可用
    var targetWebsiteAvailable = true
    //默认控件可用
    var controlAvailable = true

    try {

        //第一次登录或者需要重置环境时
        if(isFirstTime() || needResetEnvironment()) {
            if (javaFuncs.openHomePage) {
                //打开首页
                javaFuncs.openHomePage()
            }
        }

        //校验点
        context.put("validatePoint", "proxyAvailable")

        if(!Boolean.valueOf(context.get("proxyAvailable"))) {

            proxyAvailable = javaFuncs.checkProxyAvailable()
            context.put("proxyAvailable", proxyAvailable)

            var proxyInfo = context.get(LUA_TASK_PROXY_INFO);
            logger.info("任务：【 {} 】{}可用性检查【 {} 】", context.get(LUA_TASK_KEY), proxyInfo.length !== 2 ? "本机IP" : "代理IP", proxyAvailable ? "通过" : "未通过")

        }

        //代理可用
        if (proxyAvailable){

            if(!Boolean.valueOf(context.get("targetWebsiteAvailable"))) {

                context.put("validatePoint", "targetWebsiteAvailable")
                //检查目标网站可用性
                targetWebsiteAvailable = javaFuncs.checkTargetWebsiteAvailable()
                context.put("targetWebsiteAvailable", targetWebsiteAvailable)

                logger.info("任务：【 {} 】目标网站可用性检查【 {} 】", context.get(LUA_TASK_KEY), targetWebsiteAvailable ? "通过" : "未通过")
            }
            //目标网站能打开
            if (targetWebsiteAvailable) {

                //只有在用到luaclient时才需要检查控件
                if(jsCrawler.config.isLuaClient()) {

                    context.put("validatePoint", "controlAvailable")
                    //控件检查
                    controlAvailable = javaFuncs.checkControlAvailable()
                    context.put("controlAvailable", controlAvailable)
                    logger.info("任务：【 {} 】银行控件可用性检查【 {} 】", context.get(LUA_TASK_KEY),  controlAvailable ? "通过" : "未通过")

                    if(!controlAvailable) {
                        context.put("currentErrorCode", BALO_99604_55)
                    }

                }
            } else {
                context.put("currentErrorCode", BALO_21401_10)
            }

        } else {
            context.put("currentErrorCode", BALO_99601_10)
            feedBack(context)
        }
    } catch(e){
        //可以重试的异常均返回false，以便重试
        logger.error("任务：【 {} 】环境校验出错，校验点：【 {} 】", context.get(LUA_TASK_KEY), allAvailability[context.get("validatePoint")])
        context.put(context.get("validatePoint"), false)
        if(!e.getErrorCode) {
            if(e instanceof UnirestException) {
                logger.error("任务：【 {} 】网络故障，异常详情:", context.get(LUA_TASK_KEY), e)
                return false
            } else {
                throw e
            }
        }
        return false
    }

    //只要有一个为false就返回false
    return proxyAvailable && controlAvailable && targetWebsiteAvailable
}

/**
 * 刷新，重置环境
 * @param javaFuncs
 */
function resetEnvironment(javaFuncs){
    var times = 1
    while (times <= 4 && !validateEnvironment(javaFuncs)){
        //重试三次全部结束或者目标网站不可以用
        if (times === 4 || (context.targetWebsiteAvailable !== null && !context.targetWebsiteAvailable)) {
            var validatePoint = context.get("validatePoint")

            //logger.info("任务：【 {} 】当前环境校验点为【 {} 】", context.get(LUA_TASK_KEY), allAvailability[validatePoint])
            if (!Boolean.valueOf(context.get(validatePoint))) {
                environmentErrorHandleMappings[validatePoint]()
            }
        }
        logger.info("任务：【 {} 】第【 {} 】次重置环境中。。。", context.get(LUA_TASK_KEY), Integer.valueOf(times))
        times++
    }
}

/**
 * 判断当前错误是否可以重试
 * @returns {boolean} true 可以 false 不可以
 */
function canRetry(){
    var currentErrorCode = context.get("currentErrorCode")

    // eslint-disable-next-line no-undefined
    return canRetryErrorCodeRetryTime[currentErrorCode] !== undefined
}

/**
 * 判断是否第一次尝试登录，第一次是错误码为空
 * @returns {boolean} true 是第一次 false 不是第一次
 */
function isFirstTime() {
    return context.get("currentErrorCode") === null
}

/**
 * 需要重置环境的场景
 * @param javaFuncs
 * @returns {boolean|*}
 */
function needResetEnvironment(){

    var javaFuncs = context.get("javaFuncs")
    var resetOrNot = javaFuncs.needResetEnvironment ? javaFuncs.needResetEnvironment() : false

    var currentErrorCode = context.get("currentErrorCode")
    return resetOrNot
        || equals(currentErrorCode, null)
        //密码因控件输入错误
        || equals(currentErrorCode, BALO_99601_20)
        //插件未安装
        || equals(currentErrorCode, BALO_99604_55)
        //登录超时
        || equals(currentErrorCode, BALO_99601_30)
        //会话超时
        || equals(currentErrorCode, BACR_99602_20)
        //代理IP异常
        || equals(currentErrorCode, BALO_99601_10)

}

/**
 * 判断是否要重新分发到其他runtime
 * @returns {*}
 */
function needChangeRuntime() {
    var changeRuntime = equals(context.get("currentErrorCode"), BALO_99601_30)
    context.put("needChangeRuntime", String.valueOf(changeRuntime))
    return changeRuntime
}

function isChangeLimit(){
    var runtimeChangeTime = context.get("sourceMsg").get("runtimeChangeTime")
    return runtimeChangeTime == 3
}

function doAuthenticate(javaFuncs) {

    //当前错误重试次数
    var currentErrorRetryTime = 1
    var retryTimeLimit = 4;

    context.put("javaFuncs", javaFuncs)

    // var times = 1
    while(currentErrorRetryTime <= retryTimeLimit) {

        //重试次数不超过上限  不是第一次执行且不可以重试
        if(currentErrorRetryTime === retryTimeLimit || context.get("currentErrorCode") !== null && !canRetry()) {

            var errorMsg
            if(currentErrorRetryTime === retryTimeLimit) {
                errorMsg = errorMsgMap.get(context.get("currentErrorCode")) + "次数已达上限，请重新登录！"
                logger.error("任务：【 {} 】{}次因【 {} 】而重试全部失败，结束流程！", context.get(LUA_TASK_KEY), retryTimeLimit, errorMsgMap.get(context.get("currentErrorCode")))
            }
            if(!canRetry()){
                errorMsg = context.get("currentErrorMsg")
                logger.error("任务：【 {} 】【 {} 】为不可重试异常，结束流程！", context.get(LUA_TASK_KEY), errorMsgMap.get(context.get("currentErrorCode")))
            }

            // throwBankExceptionByErrorCodeAndCustomMessage(context.get("currentErrorCode"), context.get("currentErrorMsg"))
            throwBankExceptionByErrorCodeAndCustomMessage(context.get("currentErrorCode"), errorMsg)
        }

        //需要时重置环境
        if(needResetEnvironment()) {
            resetEnvironment(javaFuncs)
        }

        //只需在第一次校验密码
        if(isFirstTime()) {
            validatePassword()
        }
        //登录
        if(doLogin(javaFuncs)) { break }
        //判断是否可以重试（重新登录）
        if(canRetry()) {
            var currentErrorCode = context.get("currentErrorCode")

            currentErrorRetryTime = canRetryErrorCodeRetryTime[currentErrorCode][0]
            retryTimeLimit = canRetryErrorCodeRetryTime[currentErrorCode][1]
            currentErrorRetryTime = currentErrorRetryTime + 1
            if(currentErrorRetryTime !== retryTimeLimit) {
                logger.info("任务：【 {} 】第【 {} 】次因【 {} 】重试登录。。。",
                    context.get(LUA_TASK_KEY),
                    Integer.valueOf(currentErrorRetryTime),
                    errorMsgMap.get(currentErrorCode))
            }

            canRetryErrorCodeRetryTime[currentErrorCode][0] = currentErrorRetryTime

        }

    }

}

/**
 * 加载抓取相关的JS
 *
 */
function loadJSForCrawl() {
    load('js/flows.js')
}

/**
 * 登录
 * @param javaFuncs
 * @returns {boolean}
 */
function doLogin(javaFuncs){
    try {

        //需要刷新首页 刷新首页
        if (javaFuncs.needRefreshHomePageOrNot && javaFuncs.needRefreshHomePageOrNot()) {
            if(jsCrawler.config.isLuaClient()) {
                refresh()
            }
        }
        //恢复初始状态 开始登陆
        javaFuncs.login()
        //登录成功不需要切换runtime
        context.put("needChangeRuntime", "false")

       // loadJSForCrawl()
        //登录成功后跳出
        return true
    } catch(e){
        if(e.getErrorCode) {
            //有错误码的要将错误码放到上下文
            context.put("currentErrorCode", e.getErrorCode())
            context.put("currentErrorMsg", e.getErrorMessage())
            if(needChangeRuntime()) {
                if(isChangeLimit()){
                    //切换达到上限不需要切换runtime
                    context.put("needChangeRuntime", "false")
                    throw e
                } else {
                    return true
                }

            }
        } else if(e instanceof UnirestException){
            context.put("currentErrorCode", BALO_99601_10)
            context.put("currentErrorMsg", "代理IP异常")
        } else {
            logger.error("任务：【 {} 】非业务异常直接抛出终止流程", context.get(LUA_TASK_KEY), e)
            throw e

        }
        return false
    }
}

function transferTopic(){
    var topicAndMsg = new HashMap()
    if(equals(context.get(LUA_TASK_BANK_NAME), "PSBC")){
        topicAndMsg.put("topic", "CRED_PSBC_CHANNEL")
    } else {
        topicAndMsg.put("topic", "CRED_CHANNEL")
    }


    var sourceMsg = context.get("sourceMsg")
    sourceMsg.put("runtimeChangeTime", Integer.valueOf(sourceMsg.get("runtimeChangeTime") + 1))
    topicAndMsg.put("msg", JSON.toJSONString(sourceMsg))
    return topicAndMsg
}

var passwordValidateRuleMappings = {

    CGB : {
        CREDITCARD :{
            pc1: function(password){
                var unLegalPassword = "!\"#$%&'()*+,-./0123456789:<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
                if (StringUtils.isBlank(password)) {
                    logger.info("密码是空白字符");
                    return false;
                }
                for (var i = 0; i < password.length; i++) {
                    var c = password.charAt(i);
                    if (unLegalPassword.indexOf(c) == -1) {
                        logger.info("密码不合法,不能包含 [{}]", c);
                        return false;
                    }
                }
                return !(password.length<6 || password.length>30);
            }
        },
        DEBITCARD :{
            pc1: function (password) {
                return password.length>6;
            }
        }
    },
    PSBC :{
        CREDITCARD :{
            pc1: function(password){
                return (password.length == 6 || 8 <= password.length && password.length <= 20);
            }
        },
        DEBITCARD :{
            pc1: function (password) {
                return (password.length == 6 || 8 <= password.length && password.length <= 20);
            }
        }
    },
    CCB :{
        CREDITCARD :{
            pc1: function (password) {
                return password.length>=6 && password.length<=12;
            }
        },
        DEBITCARD: {
            pc1:function (password) {
                return password.length>=6 && password.length<=12;
            }
        }
    },
    CEB :{
        CREDITCARD :{
            pc1: function (password) {
                return password.length>=6;
            },
            pc2:function (password) {
                return password.length>=6;
            }
        },
        DEBITCARD: {
            pc1:function (password) {
                return password.length>=6;
            },
            pc2:function (password) {
                return password.length>=6;
            }
        }
    },
    PINGAN :{
        CREDITCARD :{
            pc1: function (password) {
                return password.length>=6;
            },
            wap: function (password) {
                return password.length>=6;
            },
            pc2:function (password) {
                return password.length>=6;
            },
            pc3: function (password) {
                return password.length>=6;
            }
        },
        DEBITCARD: {
            pc1:function (password) {
                return password.length>=6;
            },
            pc2:function (password) {
                return password.length>=6;
            },
            pc3: function (password) {
                return password.length>=6;
            },
            pc4: function (password) {
                return password.length>=6;
            }
        }
    },
    ABC :{
        CREDITCARD :{
            pc1: function (password) {
                return password.length>=6;
            }
        },
        DEBITCARD: {
            pc1:function (password) {
                return password.length>=6;
            }
        }
    },
    CITIC :{
        CREDITCARD :{
            pc1: function (password) {
                return password.length>=6;
            },
            pc2: function (password) {
                return password.length>=6;
            }
        },
        DEBITCARD: {
            pc3:function (password) {
                return password.length>=6;
            }
        }
    },
    BOC :{
        CREDITCARD :{
            wap: function (password) {
                return password.length>=6;
            },
            pc1:function (passwrod) {
                return passwrod.length>=6;
            }
        },
        DEBITCARD: {
            pc1:function (password) {
                return password.length>=6;
            }
        }
    },
    SHANGHAI :{
        CREDITCARD :{
            pc1: function (password) {
                return password.length>=6;
            }
        },
        DEBITCARD: {
            pc1:function (password) {
                return password.length>=6;
            }
        }
    },
    COMM :{
        CREDITCARD :{
            pc1: function (password) {
                return password.length>=6;
            },
            wap: function (password) {
                var unLegalPassword = "!\"#$%&'()*+,-./0123456789:<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
                if (StringUtils.isBlank(password)) {
                    logger.info("密码是空白字符");
                    return false;
                }
                for (var i = 0; i < password.length; i++) {
                    var c = password.charAt(i);
                    if (unLegalPassword.indexOf(c) == -1) {
                        logger.info("密码不合法,不包含 [{}]", c);
                        return false;
                    }
                }
                return !(password.length<6 || password.length>20);
            }
        },
        DEBITCARD: {
            pc1:function (password) {
                return password.length>=6;
            }
        }
    },
    CMB :{
        CREDITCARD :{
            pc1: function (password) {
                return password.length>=6;
            },
            wap2: function (password) {
                return password.matches("[0-9]{6}");
            }
        },
        DEBITCARD: {
            pc1:function (password) {
                return password.length>=6;
            }
        }
    },
    CIB :{
        CREDITCARD :{
            wap: function (password) {
                return password.matches("[0-9]{6}");
            }
        },
        DEBITCARD: {
            wap:function (password) {
                return password.matches("[0-9]{6}");
            }
        }
    },
    ICBC :{
        CREDITCARD :{
            pc1: function (password) {
                var removeSpaceAfterPwd = RegexUtils.GetText(password, "\\S*");
                logger.info("{},去掉空格的密码长度:{}",context.get(LUA_TASK_KEY),removeSpaceAfterPwd.length());
                if (removeSpaceAfterPwd.length() != password.length()) {
                    throw new BankException(BankConstants.BALO_22001_10, "用户名或密码错误");
                }

                return password.length>=6&&password.length<=30;
            }
        },
        DEBITCARD: {
            pc1:function (password) {
                var removeSpaceAfterPwd = RegexUtils.GetText(password, "\\S*");
                logger.info("{},去掉空格的密码长度:{}",context.get(LUA_TASK_KEY),removeSpaceAfterPwd.length());
                if (removeSpaceAfterPwd.length() != password.length()) {
                    throw new BankException(BankConstants.BALO_22001_10, "用户名或密码错误");
                }
                return password.length>=6&&password.length<=30;
            }
        }
    },
    BEIJING :{
        CREDITCARD :{
            pc1: function (password) {
                return password.matches("[0-9]{6}");
            }
        },
        DEBITCARD: {
            pc2:function (password) {
                return password.matches("[0-9]{6}");
            }
        }
    },
    CMBC :{
        CREDITCARD :{
            pc1: function (password) {
                return password.length>=6;
            }
        },
        DEBITCARD: {
            pc1:function (password) {
                return password.length>=6;
            }
        }
    },
    SPD :{
        CREDITCARD :{
            pc1: function (password) {
                return password.length>=6;
            },
            pc3: function (password) {
                return password.length>=6;
            }
        },
        DEBITCARD: {
            pc1: function (password) {
                return password.length>=6;
            },
            pc3: function (password) {
                return password.length>=6;
            }
        }
    },
    HXB :{
        CREDITCARD :{
            pc1: function (password) {
                return password.length>=6;
            }
        },
        DEBITCARD: {
            pc1:function (password) {
                return password.length>=6;
            }
        }
    },
    debug:{
        CREDITCARD :{
            pc1: function (password) {
                return true;
            }
        },
        DEBITCARD: {
            pc1:function (password) {
                return true;
            }
        }
    }
}

function validatePassword(){
    var isCorrectFuncs = function(password){

        var cardType = context.get(LUA_TASK_CARD_TYPE);
        var entrance = context.get("entrance")
        var bankName = context.get(LUA_TASK_BANK_NAME)
        var isCorrectFuncs = function(){

            return (passwordValidateRuleMappings[bankName]
                ?  passwordValidateRuleMappings[bankName][cardType]
                    ? passwordValidateRuleMappings[bankName][cardType][entrance]
                    : undefined
                : undefined) !== undefined
        }

        var getErrorMsgFuncs = function(){
            return errorMsgMap.get(BALO_99605_10)
        }
        assertTrueOrThrowException(null, isCorrectFuncs, getErrorMsgFuncs)
        return passwordValidateRuleMappings[bankName][cardType][entrance](password);
    };
    var getErrorMsgFuncs = function () {
        return "用户名密码校验未通过";
    };

    logger.info("任务：【 {} 】接收到的密码的长度【 {} 】", context.get(LUA_TASK_KEY), context.get(LUA_TASK_TOKEN) !== null ? context.get(LUA_TASK_TOKEN).length : 0)
    var passwordValid = assertTrueOrThrowException(context.get(LUA_TASK_TOKEN),isCorrectFuncs,getErrorMsgFuncs)
    logger.info("任务：【 {} 】密码合法性检查【 {} 】", context.get(LUA_TASK_KEY), passwordValid ? "通过" : "未通过")
}
